{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import OrderedDict\n",
    "import warnings\n",
    "from IPython.display import display\n",
    "import sympy as sp\n",
    "import matplotlib.pyplot as plt\n",
    "from chempy.chemistry import Equilibrium, ReactionSystem, Substance\n",
    "from chempy.kinetics.integrated import binary_rev\n",
    "from chempy.kinetics.ode import get_odesys\n",
    "%matplotlib inline\n",
    "sp.init_printing()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t, kf, kb, X, Y, Z = sp.symbols('t k_f k_b X Y Z')\n",
    "binary_rev(t, kf, kb, Y, Z, X, sp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eq = Equilibrium.from_string('Fe+3 + SCN- = FeSCN+2; 10**2.065')\n",
    "rsys = ReactionSystem(eq.as_reactions(kf=900))\n",
    "rsys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "odesys, extra = get_odesys(rsys)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_against_analytic(odsy, c0, tend=1, integrator='cvode', atol=1e-12, rtol=1e-10):\n",
    "    xout, yout, info = odsy.integrate(tend, c0, integrator=integrator, atol=atol, rtol=rtol)\n",
    "    ref = binary_rev(xout[1:], rsys.rxns[0].param, rsys.rxns[1].param, c0['FeSCN+2'], c0['Fe+3'], c0['SCN-'])\n",
    "    {k: v for k, v in info.items() if not k.startswith('internal')}\n",
    "    plt.figure(figsize=(14, 4))\n",
    "    plt.subplot(1, 2, 1)\n",
    "    plt.plot(xout[1:], ref, alpha=0.3, linewidth=3)\n",
    "    _ = odsy.plot_result()\n",
    "    plt.subplot(1, 2, 2)\n",
    "    plt.plot(xout[1:], yout[1:, odsy.names.index('FeSCN+2')] - ref)\n",
    "    ref[:3], ref[-3:]\n",
    "    return {k: v for k, v in info.items() if not k.startswith('internal')}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "conc = {'Fe+3': 10e-3, 'SCN-': 2e-3, 'FeSCN+2': 2e-4}\n",
    "plot_against_analytic(odesys, conc)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below are the derivations for ``binary_rev``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, a, b, c, U, V, S = sp.symbols('x a b c U V S')\n",
    "u, v = 2*a*x + b, sp.sqrt(b**2 - 4*a*c)  # b**2 > 4*a*c\n",
    "Prim = sp.log((U-V)/(U+V))/V  # primitive recip. 2nd order polynomial\n",
    "prim = sp.log((u-v)/(u+v))/v  # primitive recip. 2nd order polynomial\n",
    "prim.diff(x).simplify()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y = Y + X - x\n",
    "z = Z + X - x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dxdt = kf*y*z - kb*x\n",
    "dxdt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dxdt.expand()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr2 = dxdt.expand().collect(x)\n",
    "expr2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "coeffs = expr2.as_poly(x).coeffs()\n",
    "coeffs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eq = (sp.exp(Prim) - sp.exp(t + S))\n",
    "eq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sp.Eq(Prim*V, (t+S)*V)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eq = sp.exp(Prim*V) - sp.exp((t+S)*V)\n",
    "eq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eq2 = eq.subs({U: u})\n",
    "eq2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sol, = sp.solve(eq2, x)\n",
    "sol"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s, = sp.solve(sol.subs(t, 0) - X, S)\n",
    "s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sol2 = sol.subs(S, s).simplify()\n",
    "sol2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "R, W = sp.symbols('R W')\n",
    "r = b + 2*X*a\n",
    "w = sp.exp(-V*t)\n",
    "sol3 = sol2.subs({w: W, r: R}).simplify().collect(W)\n",
    "sol3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "exprs = [\n",
    "    sp.Eq(x, sol3),\n",
    "    sp.Eq(R, r),\n",
    "    sp.Eq(W, w),\n",
    "    sp.Eq(V, v),\n",
    "    sp.Eq(a, coeffs[0]),\n",
    "    sp.Eq(b, coeffs[1]),\n",
    "    sp.Eq(c, coeffs[2]),\n",
    "]\n",
    "for expr in exprs:\n",
    "    display(expr)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "master = exprs[0]\n",
    "for e in exprs[1:]:\n",
    "    master = master.subs(e.lhs, e.rhs)\n",
    "master"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cses, (reform,) = sp.cse(master)\n",
    "reform"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cses"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ret = 'return ' + str(reform.rhs).replace('k_f', 'kf').replace('k_b', 'kb')\n",
    "for key, val in cses:\n",
    "    print('%s = %s' % (key, str(val).replace('k_f', 'kf').replace('k_b', 'kb').replace('exp', 'be.exp').replace('sqrt', 'be.sqrt')))\n",
    "print(ret)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with warnings.catch_warnings():\n",
    "    warnings.filterwarnings('error')\n",
    "    binary_rev(1.0, 1e10, 1e-4, 0, 3e-7, 2e-7)  # check that our formulation avoids overflow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below we will test ``get_native``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from chempy.kinetics._native import get_native"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print({k: v.composition for k, v in rsys.substances.items()})\n",
    "rsys.substances = OrderedDict([(k, Substance.from_formula(v.name)) for k, v in rsys.substances.items()])\n",
    "print({k: v.composition for k, v in rsys.substances.items()})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nsys = get_native(rsys, odesys, 'gsl')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_against_analytic(nsys, conc, integrator='gsl', atol=1e-16, rtol=1e-16)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
